package com.rcache.core;

import org.apache.log4j.Logger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

/**
 * @desc 缓存中最基本的元素
 * @author gzy
 * @time 2022-05-24
 */
public class Element<K,V> implements Serializable, Cloneable {
    private static final long serialVersionUID = 1098572221286888588L;
    /**日志处理*/
    private static final Logger LOG = Logger.getLogger(Element.class.getName());
    /**定义一个 AtomicLongFieldUpdater对hitCount进行原子操作 节省内存空间*/
    private static final AtomicLongFieldUpdater<Element> HIT_COUNT_UPDATER = AtomicLongFieldUpdater.newUpdater(Element.class, "hitCount");
    /**在Element中保留key，目的是为了方便序列化*/
    private final K key;
    private V value;
    /**版本,目的是实现乐观锁。自增长*/
    private volatile long version;
    /**命中次数*/
    private volatile long hitCount;
    /**创建时间*/
    private transient long creationTime;
    /**最后访问时间*/
    private transient long lastAccessTime;
    /**最后更新时间*/
    private volatile long lastUpdateTime;



    /**
     * @param key 键
     * @param value 值
     * @param version 版本
     */
    public Element(K key, V value, long version) {
        this.key = key;
        this.value = value;
        this.version = version;
        HIT_COUNT_UPDATER.set(this, 0L);
        this.creationTime = this.getCurrentTime();
    }


    /**
     * @param key 键
     * @param value 值
     * @param version 版本
     * @param creationTime 创建时间
     * @param lastAccessTime 最后访问时间
     * @param lastUpdateTime 最后更新时间
     * @param hitCount 命中次数
     */
    public Element(K key, V value, long version, long creationTime, long lastAccessTime, long lastUpdateTime, long hitCount) {
        this.key = key;
        this.value = value;
        this.version = version;
        this.lastUpdateTime = lastUpdateTime;
        HIT_COUNT_UPDATER.set(this, hitCount);
        this.creationTime = creationTime;
        this.lastAccessTime = lastAccessTime;
    }



    /**
     * @desc 只设置 key value 使用默认版本号1L
     * @param key 键
     * @param value 值
     */
    public Element(K key, V value) {
        this(key, value, 1L);
    }


    /**
     * @desc 获取该元素的键
     * @return key
     */
    public final K getObjectKey() {
        return this.key;
    }


    /**
     * @desc 获取该元素的值
     * @return
     */
    public final V getObjectValue() {
        return this.value;
    }
    public void setObjectValue(V newValue){
        this.value=newValue;
    }
    /**
     * @desc 重写equals方法 对比key值
     * @param object
     * @return
     */
    public final boolean equals(Object object) {
        //判断是否为空
        if (object != null && object instanceof Element) {
            Element element = (Element)object;
            return this.key != null && element.getObjectKey() != null ? this.key.equals(element.getObjectKey()) : false;
        } else {
            return false;
        }
    }



    /**
     * @dsec    重写hashCode方法，返回key的hash
     * @return
     */
    @Override
    public final int hashCode() {
        return this.key.hashCode();
    }

    /**
     * @desc 设置版本号
     * @param version
     */
    public final void setVersion(long version) {
        this.version = version;
    }

    /**
     * @desc 获取当前时间戳
     * @return
     */
    public final long getCreationTime() {
        return this.creationTime;
    }

    /**
     * @desc 获取最后一次操作的时间
     * @return
     */
    public final long getLatestOfCreationAndUpdateTime() {
        return 0L == this.lastUpdateTime ? this.creationTime : this.lastUpdateTime;
    }

    /**
     * @desc 获取版本号
     * @return
     */
    public final long getVersion() {
        return this.version;
    }

    /**
     * @desc 获取最后访问时间
     * @return
     */
    public final long getLastAccessTime() {
        return this.lastAccessTime;
    }

    /**
     * @desc 获取最后访问时间
     * @return
     */
    public final void updateLastAccessTime() {
        this.lastAccessTime=System.currentTimeMillis();
    }

    /**
     * @desc 获取命中次数
     * @return
     */
    public final long getHitCount() {
        return this.hitCount;
    }
    public final void increaseHitCount() {
        this.hitCount++;
    }
    /**
     * @desc 重置访问统计
     */
    public final void resetAccessStatistics() {
        this.lastAccessTime = this.getCurrentTime();
        HIT_COUNT_UPDATER.set(this, 0L);
    }

    /**
     * @desc 更新访问统计
     */
    public final void updateAccessStatistics() {
        this.lastAccessTime = this.getCurrentTime();
        HIT_COUNT_UPDATER.incrementAndGet(this);
    }

    /**
     * @desc  更新更新统计  没看明白
     */
    public final void updateUpdateStatistics() {
        this.lastUpdateTime = this.getCurrentTime();
        this.version = this.lastUpdateTime;
    }

    /**
     * @desc 重写toString 且设为final
     * @return
     */
    public final String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ key = ").append(this.key).append(", value=").append(this.value).append(", version=").append(this.version).append(", hitCount=").append(this.hitCount).append(", CreationTime = ").append(this.getCreationTime()).append(", LastAccessTime = ").append(this.getLastAccessTime()).append(" ]");
        return sb.toString();
    }

    /**
     * @desc  计算序列化后的大小
     * @return
     */
    public final long getSerializedSize() {
        if (!this.isSerializable()) {
            return 0L;
        } else {
            long size = 0L;
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream oos = null;

            try {
                oos = new ObjectOutputStream(bout);
                oos.writeObject(this);
                size = (long)bout.size();
                long var5 = size;
                return var5;
            } catch (IOException var16) {
                LOG.debug("Error measuring element size for element with key " + this.key + ". Cause was: " + var16.getMessage());
            } finally {
                try {
                    if (oos != null) {
                        oos.close();
                    }
                } catch (Exception var15) {
                    LOG.error("Error closing ObjectOutputStream");
                }

            }

            return size;
        }
    }
    /**
     * @desc 判断key与value都是可序列化的
     * @return
     */
    public final boolean isSerializable() {
        return this.isKeySerializable() && (this.value instanceof Serializable || this.value == null);
    }

    /**
     * @desc 判断key 是可序列化的
     * @return
     */
    public final boolean isKeySerializable() {
        return this.key instanceof Serializable || this.key == null;
    }

    /**
     * @desc 获取最近更新时间
     * @return
     */
    public long getLastUpdateTime() {
        return this.lastUpdateTime;
    }
    /**
     * @desc 更新最近更新时间
     * @return
     */
    public void updateLastUpdateTime() {
        this.lastUpdateTime=System.currentTimeMillis();
    }
    /**
     * @desc 获取当前时间戳
     * @return 当前时间戳
     */
    long getCurrentTime() {
        return System.currentTimeMillis();
    }
    /**序列化*/
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeLong(this.creationTime);
        out.writeLong(this.lastAccessTime);
    }
    /**反序列化*/
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        this.creationTime = in.readLong();
        this.lastAccessTime =  in.readLong();
    }
}
